//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.IO;
using ER = Microsoft.Research.DataLayer;


namespace Microsoft.Research.DataLayer
{
    public partial class Activity : ISecurableObject, ISecurableObjectInternal
    {
        /// <summary>
        /// The security object.
        /// </summary>
        private ISecurity security;

        /// <summary>
        /// Owner of the object.
        /// </summary>
        /// <value></value>
        public ISecurityPrincipal Owner
        {
            get
            {
                if (this.OwnerGroupPrincipal != null)
                {
                    return this.OwnerGroupPrincipal;
                }
                else
                {
                    return this.OwnerUserPrincipal;
                }
            }
            set
            {
                if (value is User)
                {
                    this.OwnerUserPrincipal = (User)value;
                }
                else
                {
                    this.OwnerGroupPrincipal = (Group)value;
                }
            }
        }

        /// <summary>
        /// Gets the security.
        /// </summary>
        /// <value>The security.</value>
        public ISecurity Security { get { return security; } }


        #region Activity specific permissions management

        /// <summary>
        /// The ISecurity class implementation.
        /// </summary>
        public class ISecurity
        {
            /// <summary>
            /// The secuarable object.
            /// </summary>
            private ISecurableObjectInternal securableObj;
            /// <summary>
            /// The connection object.
            /// </summary>
            private Connection connection;
            /// <summary>
            /// Permissions available.
            /// </summary>
            RunWorkflowPermission runWFPerm;
            AuthorWorkflowPermission authWFPerm;

            internal ISecurity(Activity obj, Connection c)
            {
                this.securableObj = obj;
                connection = c;
                runWFPerm = new RunWorkflowPermission(obj, c);
                authWFPerm = new AuthorWorkflowPermission(obj, c);
                IPermissions.RunWorkflow = runWFPerm;
                IPermissions.AuthorWorkflow = authWFPerm;
                Rights = new IRights();
                Rights.AuthorWorkflow = authWFPerm;
                Rights.RunWorkflow = runWFPerm;
            }

            /// <summary>
            /// The IPermission interface.
            /// </summary>
            public interface IPermission : ER.IPermission
            {
            }

            /// <summary>
            /// The IPrivilege interface.
            /// </summary>
            public interface IPrivilege : ER.IPrivilege
            {

            }

            /// <summary>
            /// The AllowDenyPermission base class.
            /// </summary>
            public class AllowDenyPermission : AllowDenyPermissionBase<IPrivilege>
            {
                public AllowDenyPermission(ISecurableObject obj, Connection c)
                    : base(obj, c)
                {

                }
            }

            # region RunWorkflowPermission

            /// <summary>
            /// The RunWorkflow permission implementation
            /// </summary>
            public class RunWorkflowPermission : AllowDenyPermission, IPermission
            {
                private string uilocres = "Properties.Resource1.RunWorkflowText";
                private string noloctxt = "Run Workflow";

                public RunWorkflowPermission(ISecurableObject obj, Connection c)
                    : base(obj, c)
                {
                    base.Allow = new Allow(this, obj, c);
                    base.Deny = new Deny(this, obj, c);
                }

                #region IPermission Members

                public string DisplayName
                {
                    get
                    {
                        try
                        {
                            string locString = Properties.Resource1.ResourceManager.GetString(uilocres.Substring(uilocres.LastIndexOf('.') + 1));
                            if (!String.IsNullOrEmpty(locString))
                            {
                                return locString;
                            }
                        }
                        catch
                        {

                        }
                        return noloctxt;
                    }
                }
                #endregion
            }

            # endregion

            # region AuthorWorkflow Permission

            /// <summary>
            /// The AuthorWorkflow permission implementation
            /// </summary>
            public partial class AuthorWorkflowPermission : AllowDenyPermission, IPermission
            {
                private string uilocres = "Properties.Resource1.AuthorWorkflowText";
                private string noloctxt = "Author Workflow";

                public AuthorWorkflowPermission(ISecurableObject obj, Connection c)
                    : base(obj, c)
                {
                    base.Allow = new Allow(this, obj, c);
                    base.Deny = new Deny(this, obj, c);
                }

                #region IPermission Members

                public string DisplayName
                {
                    get
                    {
                        try
                        {
                            string locString = Properties.Resource1.ResourceManager.GetString(uilocres.Substring(uilocres.LastIndexOf('.') + 1));
                            if (!String.IsNullOrEmpty(locString))
                            {
                                return locString;
                            }
                        }
                        catch
                        {

                        }
                        return noloctxt;
                    }
                }

                #endregion
            }

            # endregion

            /// <summary>
            /// The IPermissions class holding static instances of the Permission objects.
            /// </summary>
            public class IPermissions
            {
                public static RunWorkflowPermission RunWorkflow;
                public static AuthorWorkflowPermission AuthorWorkflow;
            }

            /// <summary>
            /// The IRights class holding instances of the Permission objects.
            /// </summary>
            public class IRights
            {
                public RunWorkflowPermission RunWorkflow;
                public AuthorWorkflowPermission AuthorWorkflow;
            }

            /// <summary>
            /// The IRights object.
            /// </summary>
            public IRights Rights;

            /// <summary>
            /// Checks whether the principal holds the particular privilege.
            /// </summary>
            /// <param name="principal">The principal.</param>
            /// <param name="priv">The priv.</param>
            /// <returns></returns>
            public bool CheckAccess(ISecurityPrincipal principal, IPrivilege priv)
            {
                return securableObj.CheckAccess(principal, priv);
            }

            /// <summary>
            /// Adds access to the user for the given privilege.
            /// </summary>
            /// <param name="principal">The principal.</param>
            /// <param name="priv">The priv.</param>
            public void AddAccess(ISecurityPrincipal principal, IPrivilege priv)
            {
                securableObj.AddAccess(principal, priv);
            }

            /// <summary>
            /// Removes the access for the user for the given privilege.
            /// </summary>
            /// <param name="principal">The principal.</param>
            /// <param name="priv">The priv.</param>
            public void RemoveAccess(ISecurityPrincipal principal, IPrivilege priv)
            {
                securableObj.RemoveAccess(principal, priv);
            }

            # region Allow Privilege

            public class Allow : IPrivilege
            {
                #region IPrivilege Members

                private string uilocres = "Properties.Resource1.AllowText";
                private string noloctxt = "Allow";

                private IPermission permission;
                private Connection connection;
                private ISecurableObject obj;

                public Allow(IPermission permObj, ISecurableObject obj, Connection conn)
                {
                    permission = permObj;
                    connection = conn;
                    this.obj = obj;
                }
                #endregion

                #region IPrivilege Members

                public string DisplayName
                {
                    get
                    {
                        try
                        {
                            string locString = Properties.Resource1.ResourceManager.GetString(uilocres.Substring(uilocres.LastIndexOf('.') + 1));
                            if (!String.IsNullOrEmpty(locString))
                            {
                                return locString;
                            }
                        }
                        catch
                        {

                        }
                        return noloctxt;
                    }
                }

                public ER.IPermission Permission
                {
                    get { return permission; }
                }

                public bool Check(ISecurityPrincipal principal)
                {
                    return Activity.Check(principal, this, obj, connection);
                }

                public void Ensure(ISecurityPrincipal principal)
                {
                    if (!Check(principal))
                    {
                        throw new ApplicationException("Not Allowed");
                    }
                }

                #endregion

            }

            # endregion

            # region Deny Privilege

            public class Deny : IPrivilege
            {
                #region IPrivilege Members

                private string uilocres = "Properties.Resource1.DenyText";
                private string noloctxt = "Deny";

                private IPermission perm;
                private Connection conn;
                private ISecurableObject obj;

                public Deny(IPermission permObj, ISecurableObject obj, Connection conne)
                {
                    perm = permObj;
                    conn = conne;
                    this.obj = obj;
                }
                #endregion

                #region IPrivilege Members

                public string DisplayName
                {
                    get
                    {
                        try
                        {
                            string locString = Properties.Resource1.ResourceManager.GetString(uilocres.Substring(uilocres.LastIndexOf('.') + 1));
                            if (!String.IsNullOrEmpty(locString))
                            {
                                return locString;
                            }
                        }
                        catch
                        {

                        }
                        return noloctxt;
                    }
                }

                public ER.IPermission Permission
                {
                    get { return perm; }
                }

                public bool Check(ISecurityPrincipal principal)
                {
                    return Activity.Check(principal, this, obj, conn);
                }

                public void Ensure(ISecurityPrincipal principal)
                {
                    throw new NotImplementedException();
                }

                #endregion
            }

            # endregion

            /// <summary>
            /// Returns the array of Permission objects.
            /// </summary>
            public ER.IPermission[] Permission
            {
                get
                {
                    return securableObj.Permission;
                }
            }

        }
        #endregion

        #region ISecurableObject interface implementation

        /// <summary>
        /// Returns an array of permission objects.
        /// </summary>
        IPermission[] ISecurableObjectInternal.Permission
        {
            get
            {
                return new IPermission[] { ISecurity.IPermissions.RunWorkflow, ISecurity.IPermissions.AuthorWorkflow };
            }
        }

        /// <summary>
        /// Returns an array of SecurityPermission objects.
        /// </summary>
        public SecurityPermission[] DACL
        {
            get
            {
                return GetSecurityDACL();
            }
        }

        /// <summary>
        /// Gets the security DACL.
        /// </summary>
        /// <returns>Array of security permissions.</returns>
        private SecurityPermission[] GetSecurityDACL()
        {
            List<SecurityPermission> secPermList = new List<SecurityPermission>();

            try
            {
                ActivityACLCollection<Activity> ActACLList = this.ActivityACLs;
                foreach (ActivityACL acl in ActACLList)
                {
                    SecurityPermission secPerm = new SecurityPermission();
                    if (acl.PrivOwnerUserPrincipal != null)
                    {
                        secPerm.Principal = acl.PrivOwnerUserPrincipal;
                    }
                    else if (acl.PrivOwnerGroupPrincipal != null)
                    {
                        secPerm.Principal = acl.PrivOwnerGroupPrincipal;
                    }
                    if (acl.Permission == ActivityPermissionType.AuthorWorkflow)
                    {
                        if (acl.AuthorWorkflowPriv == ActivityAuthorWorkflowPriv.Allow)
                        {
                            secPerm.Privilege = Activity.ISecurity.IPermissions.AuthorWorkflow.Allow;
                        }
                        else if (acl.AuthorWorkflowPriv == ActivityAuthorWorkflowPriv.Deny)
                        {
                            secPerm.Privilege = Activity.ISecurity.IPermissions.AuthorWorkflow.Deny;
                        }
                    }
                    else if (acl.Permission == ActivityPermissionType.RunWorkflow)
                    {
                        if (acl.RunWorkflowPriv == ActivityRunWorkflowPriv.Allow)
                        {
                            secPerm.Privilege = Activity.ISecurity.IPermissions.RunWorkflow.Allow;
                        }
                        else if (acl.RunWorkflowPriv == ActivityRunWorkflowPriv.Deny)
                        {
                            secPerm.Privilege = Activity.ISecurity.IPermissions.RunWorkflow.Deny;
                        }
                    }
                    secPermList.Add(secPerm);
                }
            }
            catch (RecordNotFoundException)
            {

            }
            return secPermList.ToArray();
        }

        /// <summary>
        /// Adds the access.
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <param name="priv">The priv.</param>
        void ISecurableObjectInternal.AddAccess(ISecurityPrincipal principal, IPrivilege priv)
        {
            ActivityACL.Create(principal, priv, this, this.Conn);
        }

        /// <summary>
        /// Removes the access.
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <param name="priv">The priv.</param>
        void ISecurableObjectInternal.RemoveAccess(ISecurityPrincipal principal, IPrivilege priv)
        {
            List<ActivityACL> ActACLList = new List<ActivityACL>();
            try
            {
                ActivityACL.ISearch searchQuery = ActivityACL.ISearch.Create();
                searchQuery.Query = ActivityACL.ISearch.AND(
                    FetchPrincipalSearchClause(principal),
                    FetchPrivilegeClause(priv),
                    ActivityACL.ISearch.Activity(ObjectFieldBase.Condition.Equals, this));
                ActACLList = ActivityACL.Search(searchQuery, Conn);
                ActivityACL acl = ActACLList[0];
                acl.Delete();
            }
            catch (RecordNotFoundException)
            {

            }
        }

        /// <summary>
        /// Checks the access.
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <param name="priv">The priv.</param>
        /// <returns></returns>
        bool ISecurableObjectInternal.CheckAccess(ISecurityPrincipal principal, IPrivilege priv)
        {
            return priv.Permission.Check(principal);
        }

        /// <summary>
        /// Checks the privilege access for the specified principal.
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <param name="priv">The priv.</param>
        /// <param name="conn">The conn.</param>
        /// <returns></returns>
        public static bool Check(ISecurityPrincipal principal, IPrivilege priv, ISecurableObject obj, Connection conn)
        {
            List<ActivityACL> ActACLList = new List<ActivityACL>();

            try
            {
                ActivityACL.ISearch searchQuery = ActivityACL.ISearch.Create();
                searchQuery.Query = ActivityACL.ISearch.AND(
                    FetchPrincipalSearchClause(principal),
                    FetchPrivilegeClause(priv),
                    ActivityACL.ISearch.Activity(ObjectFieldBase.Condition.Equals, obj as Activity)
                    );
                ActACLList = ActivityACL.Search(searchQuery, conn);
            }
            catch (RecordNotFoundException)
            {
                return false;
            }

            if (ActACLList.Count > 0)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Fetches the principal search clause.
        /// </summary>
        /// <param name="principal">The principal.</param>
        /// <returns></returns>
        private static ActivityACL.ISearch.ISearchClause FetchPrincipalSearchClause(ISecurityPrincipal principal)
        {
            if (principal is User)
                return ActivityACL.ISearch.PrivOwnerUserPrincipal(ObjectFieldBase.Condition.Equals, principal as User);
            else
                return ActivityACL.ISearch.PrivOwnerGroupPrincipal(ObjectFieldBase.Condition.Equals, principal as Group);
        }

        /// <summary>
        /// Fetches the privilege clause.
        /// </summary>
        /// <param name="privilege">The privilege.</param>
        /// <returns></returns>
        private static ActivityACL.ISearch.ISearchClause FetchPrivilegeClause(IPrivilege privilege)
        {
            if (privilege.Permission is Activity.ISecurity.AuthorWorkflowPermission)
            {
                if (privilege is Activity.ISecurity.Allow)
                {
                    return ActivityACL.ISearch.AuthorWorkflowPriv(EnumFieldBase.Condition.Equals, ActivityAuthorWorkflowPriv.Allow);
                }
                else
                {
                    return ActivityACL.ISearch.AuthorWorkflowPriv(EnumFieldBase.Condition.Equals, ActivityAuthorWorkflowPriv.Deny);
                }
            }
            else
            {
                if (privilege is Activity.ISecurity.Allow)
                {
                    return ActivityACL.ISearch.RunWorkflowPriv(EnumFieldBase.Condition.Equals, ActivityRunWorkflowPriv.Allow);
                }
                else
                {
                    return ActivityACL.ISearch.RunWorkflowPriv(EnumFieldBase.Condition.Equals, ActivityRunWorkflowPriv.Deny);
                }
            }
        }
        #endregion

        /// <summary>
        /// Inherits the ACL information from the old version to the new version.
        /// </summary>
        /// <param name="oldVersion">Old version of the Workflow.</param>
        /// <param name="newVersion">New version of the Workflow.</param>
        /// <param name="connection">The connection object.</param>
        public static void InheritACLInfo(Activity oldVersion, Activity newVersion, Connection connection)
        {
            // Inheriting the ACL Information
            foreach (ActivityACL aclInfo in oldVersion.ActivityACLs)
            {
                ActivityACL newACL = ActivityACL.CreateACLForNewVersion(aclInfo, newVersion, connection);
                newACL.Save();
            }
        }


        public static int ComparePosition(ActivitySequence seq1, ActivitySequence seq2)
        {
            return seq1.Position.CompareTo(seq2.Position);
        }

        public List<ActivitySequence> Children
        {
            get
            {
                if (BaseSequence != null)
                {
                    return BaseSequence.Children;
                }
                return new List<ActivitySequence>();
            }
        }

        /// <summary>
        /// Return the structure of a composite activity
        /// note that we just assume that each sequence is structurally equivalent
        /// and choose the first one if it exists
        /// </summary>
        /// <value>The base sequence.</value>
        public ActivitySequence BaseSequence
        {
            get
            {
                if (this.ActivitySequences.Count > 0)
                {
                    foreach (ActivitySequence sequence in this.ActivitySequences)
                    {
                        if (sequence.IsBase)
                        {
                            return sequence;
                        }
                    }
                }
                return null;
            }
        }

        public bool IsWorkflow
        {
            get
            {
                return (Type == ActivityType.Root);
            }
        }

        public bool IsComposite
        {
            get
            {
                return (Type == ActivityType.Root || Type == ActivityType.Composite);
            }
        }

        private static int SortByPosition(ActivitySequence a1, ActivitySequence a2)
        {
            return a1.Position == a2.Position ? 0 : (a1.Position > a2.Position ? 1 : -1);
        }

        public string DisplayName
        {
            get
            {
                return string.IsNullOrEmpty(this.VersionLabel) ? this.Name : string.Format("{0} ({1})", this.Name, this.VersionLabel);
            }
        }

        protected override void OnConstructor()
        {
            base.OnConstructor();
        }

        public void DownloadBinaries(string targetFolder)
        {
            string xomlFile = Path.Combine(targetFolder, this.XomlFileName);
            File.WriteAllLines(xomlFile, new string[] { this.XomlContents });

            foreach (AssemblyPackage pkg in this.AssemblyPackages)
            {
                foreach (AssemblyImage img in pkg.AssemblyImages)
                {
                    string targetFile = Path.Combine(targetFolder, img.Filename);
                    File.WriteAllBytes(targetFile, img.Contents);
                }
            }
        }

        public string XomlFileName
        {
            get
            {
                StringBuilder xomlFile = new StringBuilder(string.Format("{0}.xoml", this.Name));

                List<char> badChars = new List<char>(Path.GetInvalidFileNameChars());
                for (int i = 0; i < xomlFile.Length; i++)
                {
                    char c = xomlFile[i];
                    if (badChars.Contains(c))
                        xomlFile[i] = '_';
                }
                return xomlFile.ToString();
            }
        }

        /// <summary>
        /// This will be used to create the next version of this activity object.
        /// We will be copying the contents of this object to a new one, increment 
        /// the version and pass it to be used as the next version. Then anyone can change it,
        /// keeping the information within this object as the base. 
        /// 
        /// </summary>
        /// <returns></returns>
        public Activity CreateChildSkeleton()
        {
            // Clone this properly. For the time being I'm copying only few things
            // best things to do here is
            //  1. change the guid of the current object
            //  2. save it 
            //  3. reload it to a different activity

            Activity activity = Activity.Create(this.Name,
                                        ActivityType.Root, this.Owner, Conn);
            activity.XomlContents = this.xomlcontents;


            activity.Description = this.Description;
            activity.Label = this.Label;
            activity.Name = this.Name;
            activity.Owner = this.Owner;
            //if (this.Version == null) this.Version = "1.0";
            //activity.Version = (double.Parse(this.Version) + 1) + ".0";


            // once we have a new version, this object will be regarded as the old version
            DateTime currentTime = DateTime.Now;

            this.Updated = new EntryChange("Fix Me", currentTime);
            activity.Created = new EntryChange("Fix Me", currentTime);

            return activity;
        }

        /// <summary>
        /// This method is used to create a package for the WF. This method
        /// will traverse the WF contents and add them to the target connection
        /// by calling the CopyTo method for this as well as the child objects.
        /// </summary>
        /// <param name="targetConnection"></param>
        public void ExportToPackage(Connection sourceConnection, Connection targetConnection)
        {
            List<IObject> objects = new List<IObject>();
            objects.Add(this);
            foreach (Tag tag in this.Tags)
                objects.Add(tag);
            foreach (ActivitySequence seq in this.ActivitySequences)
                objects.Add(seq);
            
            ExportHelper.ExportObjects(objects, sourceConnection, targetConnection);
        }

        /// <summary>
        /// Checks if the activity is the latest version.
        /// </summary>
        /// <param name="activityToValidate">The activity.</param>
        /// <returns>True if the activity is the latest version;False otherwise.</returns>
        public bool IsLatestVersion()
        {
            bool result = false;
            this.NewerVersions.GetEnumerator();
            if (this.NewerVersions.Count == 0)
            {
                result = true;
            }
            return result;
        }

        /// <summary>
        /// Deletes the icon blobs.
        /// </summary>
        public void DeleteIconBlobs()
        {
            foreach (ActivityIconBlob nsBlob in this.IconStores)
            {
                nsBlob.Delete();
            }
            this.IconStores.Refresh();
        }

        /// <summary>
        /// Get all the older versions of the workflow. Stop when V-0 is reached or a labelled version is reached.
        /// </summary>
        /// <param name="workflow">The workflow.</param>
        /// <param name="versionCollection">Collection of the versions of the workflow.</param>
        public void GetVersionsToDelete(Collection<Activity> versionCollection)
        {
            #region Validation of Arguments

            if (null == versionCollection)
            {
                throw new ArgumentNullException("versionCollection");
            }

            #endregion

            this.OlderVersions.GetEnumerator();
            if (this.OlderVersions.Count > 0)
            {
                Activity oldVersion = this.OlderVersions[0].Activity_1;
                // Add the older version to the list if not labelled.
                if (string.IsNullOrEmpty(oldVersion.VersionLabel))
                {
                    versionCollection.Add(oldVersion);
                    oldVersion.GetVersionsToDelete(versionCollection);
                }
            }
        }

        /// <summary>
        /// Gets the latest activity.
        /// </summary>
        /// <returns>The latest activity.</returns>
        public Activity GetLatestActivity()
        {
            Activity latestVersion = this;

            this.NewerVersions.GetEnumerator();
            if (this.NewerVersions.Count > 0)
            {
                ActivityVersion actVersion = this.NewerVersions[this.NewerVersions.Count - 1];
                latestVersion = actVersion.Activity_2.GetLatestActivity();
            }

            return latestVersion;
        }

        /// <summary>
        /// Gets the older activity versions.
        /// </summary>
        /// <param name="activity">The activity.</param>
        /// <param name="olderActivities">The older activities.</param>
        public static void GetOlderActivityVersions(Activity activity, List<Activity> olderActivities)
        {
            if (null == olderActivities)
            {
                olderActivities = new List<Activity>();
            }

            if (activity != null)
            {
                activity.OlderVersions.GetEnumerator();
                if (activity.OlderVersions.Count > 0)
                {
                    ActivityVersion actVersion = activity.OlderVersions[activity.OlderVersions.Count - 1];
                    Activity olderVersion = actVersion.Activity_1;
                    if (olderVersion != null
                        && !olderVersion.IsDeleted
                        && string.IsNullOrEmpty(olderVersion.VersionLabel))
                    {
                        olderActivities.Add(olderVersion);
                        Activity.GetOlderActivityVersions(olderVersion, olderActivities);
                    }
                }
            }
        }

        /// <summary>
        /// Get all ACL info for the activity
        /// </summary>
        /// <returns>The ACL info for the activity.</returns>
        public Collection<ActivityACL> GetAllACLs()
        {
            Collection<ActivityACL> sharedActivities = null;
            try
            {
                ActivityACL.ISearch searchQuery = ActivityACL.ISearch.Create();
                searchQuery.Query = ActivityACL.ISearch.Activity(ObjectFieldBase.Condition.Equals, this);
                sharedActivities = new Collection<ActivityACL>(ActivityACL.Search(searchQuery, this.Conn));
            }
            catch (RecordNotFoundException)
            {
            }

            return sharedActivities;
        }

        #region CommonSearches

        public sealed class CommonSearches
        {
            private CommonSearches()
            {
            }

            /// <summary>
            /// Fetches all the activities of type workflow in the registry that have not been deleted.
            /// </summary>
            /// <param name="connection">Registry connectionection.</param>
            /// <returns>Collection of activities of type workflow.</returns>
            public static Collection<Activity> GetAllWorkflows(Connection connection, Activity.ISearch.ISearchClause filter)
            {
                #region Validation of Arguments

                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }

                #endregion
                Collection<Activity> allWorkflows = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    Activity.ISearch.ISearchClause clause = Activity.ISearch.AND(
                        Activity.ISearch.Type(EnumFieldBase.Condition.Equals, ActivityType.Root),
                        Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false));
                    if (null != filter)
                    {
                        searchQuery.Query = Activity.ISearch.AND(clause, filter);
                    }
                    else
                    {
                        searchQuery.Query = clause;
                    }
                    allWorkflows = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                }
                return allWorkflows;
            }

            /// <summary>
            /// Fetches all the activities in the registry that have not been deleted and for teh specific category.
            /// </summary>
            /// <param name="connection">Registry connectionection.</param>
            /// <returns>Collection of activities of type workflow.</returns>
            public static Collection<Activity> GetAllActivities(Connection connection, Namespace category, Activity.ISearch.ISearchClause filter)
            {
                #region Validation of Arguments

                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }

                #endregion
                Collection<Activity> allWorkflows = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    Activity.ISearch.ISearchClause clause = Activity.ISearch.AND(
                        Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false),
                        Activity.ISearch.Namespace(RelationFieldBase.Condition.Equals, category));
                    if (null != filter)
                    {
                        searchQuery.Query = Activity.ISearch.AND(clause, filter);
                    }
                    else
                    {
                        searchQuery.Query = clause;
                    }
                    allWorkflows = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                }
                return allWorkflows;
            }

            /// <summary>
            /// Gets all activities in the registry that have not been deleted.
            /// </summary>
            /// <param name="connection">Registry connection.</param>
            /// <returns>Collection of Activities.</returns>
            public static Collection<Activity> GetAllActivities(Connection connection)
            {
                #region Validation of Arguments

                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }

                #endregion
                Collection<Activity> allActivities = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    searchQuery.Query = Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false);
                    allActivities = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                    allActivities = new Collection<Activity>();
                }
                return allActivities;
            }

            /// <summary>
            /// Fetches an activity by name from the registry.
            /// </summary>
            /// <param name="activityClass">Class of the activity to find.</param>
            /// <param name="connection">Registry connection.</param>
            /// <returns>Returns the activity found in the registry.</returns>
            public static Activity FindActivityInRegistry(string activityClass, Connection connection)
            {
                #region Validation of Arguments

                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                if (string.IsNullOrEmpty(activityClass))
                {
                    throw new ArgumentNullException("activityClass");
                }

                #endregion

                Activity regActivity = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    searchQuery.Query = Activity.ISearch.AND(
                        Activity.ISearch.ActivityClass(StringField.Condition.Equals, activityClass),
                        Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false));
                    List<Activity> activityList = Activity.Search(searchQuery, connection);
                    if (activityList != null && activityList.Count > 0)
                    {
                        foreach (Activity activity in activityList)
                        {
                            if (activity.IsLatestVersion())
                            {
                                regActivity = activity;
                                break;
                            }
                        }
                    }
                }
                catch (RecordNotFoundException)
                {
                }

                return regActivity;
            }

            /// <summary>
            /// Fetches workflows by name from the registry.
            /// </summary>
            /// <param name="workflowName">The workflow name.</param>
            /// <param name="connection">Registry connection.</param>
            /// <returns>Returns collection of workflows found in the registry.</returns>
            public static Collection<Activity> FindWorkflowInRegistry(string workflowName, Connection connection)
            {
                #region Validation of Arguments

                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }
                if (string.IsNullOrEmpty(workflowName))
                {
                    throw new ArgumentNullException("workflowName");
                }

                #endregion

                Collection<Activity> activities = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    searchQuery.Query = Activity.ISearch.AND(
                        Activity.ISearch.Type(EnumFieldBase.Condition.Equals, ActivityType.Root),
                        Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false),
                        Activity.ISearch.Name(StringField.Condition.Equals, workflowName.Trim()));
                    activities = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                }
                return activities;
            }

            /// <summary>
            /// Retreive the deleted workflows from the registry.
            /// </summary>
            /// <param name="connection">Registry connection.</param>
            /// <returns>Collection of all the deleted workflows.</returns>
            public static Collection<Activity> GetDeletedWorkflows(Connection connection, Activity.ISearch.ISearchClause filter)
            {
                #region Validation of Arguments

                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }

                #endregion

                Collection<Activity> deletedActivities = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    searchQuery.Query = Activity.ISearch.AND(
                                            Activity.ISearch.Type(EnumFieldBase.Condition.Equals, ActivityType.Root),
                                            Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, true));
                    if (null != filter)
                    {
                        searchQuery.Query = Activity.ISearch.AND(searchQuery.Query, filter);
                    }
                    deletedActivities = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                }
                return deletedActivities;
            }

            /// <summary>
            /// Gets the activities by name.
            /// </summary>
            /// <param name="connection">The connection.</param>
            /// <returns></returns>
            public static Collection<Activity> GetActivitiesByName(string name, Connection connection)
            {
                Collection<Activity> activities = null;
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    searchQuery.Query = Activity.ISearch.ActivityClass(StringField.Condition.Equals, name);
                    activities = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                }
                return activities;
            }


            /// <summary>
            /// Get all activities for the given ownerId
            /// </summary>
            /// <param name="connection">Registry Connection</param>
            /// <param name="ownerId">activity owner Id</param>
            /// <returns>Activities Collection</returns>
            public static Collection<Activity> GetAllActivitiesOfOwner(Connection connection, Guid ownerId)
            {
                if (null == connection)
                {
                    throw new ArgumentNullException("connection");
                }

                Collection<Activity> ownerActivities = null;
                try
                {
                    User createdUser = User.Load(ownerId, connection);
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    searchQuery.Query = Activity.ISearch.OwnerUserPrincipal(ObjectFieldBase.Condition.Equals, createdUser);
                    ownerActivities = new Collection<Activity>(Activity.Search(searchQuery, connection));
                }
                catch (RecordNotFoundException)
                {
                }

                return ownerActivities;

            }

            public static Collection<Activity> GetWorkflowsNotOwnedBy(Connection registryConnection, Collection<User> restrictedUsers)
            {
                Collection<Activity> allWorkflows = new Collection<Activity>();
                try
                {
                    Activity.ISearch searchQuery = Activity.ISearch.Create();
                    if (restrictedUsers != null && restrictedUsers.Count > 0)
                    {
                        Activity.ISearch.ISearchClause restrictedUsersClause = Activity.ISearch.OwnerUserPrincipal(ObjectFieldBase.Condition.NotEquals, restrictedUsers[0]);

                        for (int i = 1; i < restrictedUsers.Count; i++)
                        {
                            restrictedUsersClause =
                                Activity.ISearch.AND(restrictedUsersClause,
                                Activity.ISearch.OwnerUserPrincipal(ObjectFieldBase.Condition.NotEquals, restrictedUsers[i]));
                        }
                        searchQuery.Query = Activity.ISearch.AND(
                        Activity.ISearch.Type(EnumFieldBase.Condition.Equals, ActivityType.Root),
                        Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false), restrictedUsersClause);
                    }
                    else
                    {
                        searchQuery.Query = Activity.ISearch.AND(
                        Activity.ISearch.Type(EnumFieldBase.Condition.Equals, ActivityType.Root),
                        Activity.ISearch.IsDeleted(BooleanField.Condition.Equals, false));
                    }

                    allWorkflows = new Collection<Activity>(Activity.Search(searchQuery, registryConnection));
                }
                catch (RecordNotFoundException)
                {
                }
                return allWorkflows;
            }
        }

        #endregion


        /// <summary>
        /// Fetches the related workflows of an activity.
        /// </summary>
        /// <param name="activity">The activity.</param>
        /// <returns></returns>
        public static List<Activity> FetchRelatedWorkflows(Activity activity)
        {
            List<Activity> relatedWorkflows = new List<Activity>();
            if (null != activity)
            {
                Collection<ActivitySequence> baseSequences = new Collection<ActivitySequence>();

                foreach (ActivitySequence actSeq in activity.ActivitySequences)
                {
                    ActivitySequence baseSequence = ActivitySequence.FetchBaseActivitySequence(actSeq);
                    try
                    {
                        if (baseSequence != null && baseSequence.IsBase && !baseSequence.Activity.IsDeleted)
                        {
                            baseSequences.Add(baseSequence);
                        }
                    }
                    catch (System.Security.SecurityException)
                    {
                        // Ignore: User does not have permission for the Worklfow.
                    }
                }

                foreach (ActivitySequence wfSeq in baseSequences.Distinct(new ActivitySequenceComparer()))
                {
                    relatedWorkflows.Add(wfSeq.Activity);
                }
            }

            return relatedWorkflows;
        }

        /// <summary>
        /// Fetches all the workflows of an activity including DELETED workflows.
        /// </summary>
        /// <param name="activity">The activity.</param>
        /// <returns></returns>
        public static List<Activity> FetchAllWorkflows(Activity activity)
        {
            List<Activity> relatedWorkflows = new List<Activity>();
            if (null != activity)
            {
                Collection<ActivitySequence> baseSequences = new Collection<ActivitySequence>();

                foreach (ActivitySequence actSeq in activity.ActivitySequences)
                {
                    ActivitySequence baseSequence = ActivitySequence.FetchBaseActivitySequence(actSeq);
                    try
                    {
                        if (baseSequence != null && baseSequence.IsBase)
                        {
                            baseSequences.Add(baseSequence);
                        }
                    }
                    catch (System.Security.SecurityException)
                    {
                        // Ignore: User does not have permission for the Worklfow.
                    }
                }

                foreach (ActivitySequence wfSeq in baseSequences.Distinct(new ActivitySequenceComparer()))
                {
                    relatedWorkflows.Add(wfSeq.Activity);
                }
            }

            return relatedWorkflows;
        }

        /// <summary>
        /// Determines whether workflow has associted jobs.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if [has associted jobs]; otherwise, <c>false</c>.
        /// </returns>
        public bool HasAssocitedJobs()
        {
            this.Instances.GetEnumerator();
            return this.Instances.Count > 0;
        }

        /// <summary>
        /// Deletes the activity and its parameters
        /// </summary>
        /// <param name="activityToDelete">Activity</param>
        public void DeleteUnused()
        {
            // Delete the parameters.
            foreach (ActivityParameter parameter in this.ActivityParameters)
            {
                parameter.Delete();
            }

            // Delete assembly packages.
            this.AssemblyPackages.Clear();

            RePositionVersions();

            //Delete the activity itself.
            this.Delete();
        }

        /// <summary>
        /// Deletes the activity and its parameters
        /// </summary>
        /// <param name="activityToDelete">Activity</param>
        public void DeleteUnusedWorkflow()
        {
            // Delete activity sequences.
            foreach (ActivitySequence activityseq in ActivitySequences)
            {
                activityseq.DeleteActivitySequence();
            }

            // Delete associated icon blobs.
            DeleteIconBlobs();

            // Delete the activity acls.
            Collection<ActivityACL> activityAcls = GetAllACLs();

            if (activityAcls != null)
            {
                foreach (ActivityACL acl in activityAcls)
                {
                    acl.Delete();
                }
            }

            // Delete assembly packages.
            this.AssemblyPackages.Clear();

            RePositionVersions();

            //Delete the activity itself.
            this.Delete();
        }

        /// <summary>
        /// Re the arrange versions.
        /// </summary>
        private void RePositionVersions()
        {
            Activity oldVersion = null;
            this.OlderVersions.GetEnumerator();
            if (this.OlderVersions.Count > 0)
            {
                oldVersion = this.OlderVersions[0].Activity_1;
                this.OlderVersions.Clear();
            }

            Activity newVersion = null;
            this.NewerVersions.GetEnumerator();
            if (this.NewerVersions.Count > 0)
            {
                newVersion = this.NewerVersions[0].Activity_2;
                this.NewerVersions.Clear();
            }

            // Map the older and newer versions.
            if (oldVersion != null && newVersion != null)
            {
                oldVersion.NewerVersions.Clear();

                newVersion.OlderVersions.Clear();
                newVersion.SafeParentVersion = oldVersion;
                newVersion.Save();

                ActivityVersion newerVersionInfo = ActivityVersion.Create(oldVersion, newVersion, VersionRelationshipType.Concrete, Conn);
                oldVersion.NewerVersions.Add(newerVersionInfo);
            }

            if (newVersion != null && oldVersion == null)
            {
                newVersion.OlderVersions.Clear();
                newVersion.SafeParentVersion = null;

                newVersion.Save();
            }

            if (newVersion == null && oldVersion != null)
            {
                oldVersion.NewerVersions.Clear();
            }
        }
    }
}
